package com.szjz.freequery.util;

import com.szjz.freequery.annotation.FieldHidden;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.regex.Pattern.compile;

/**
 * author:史博
 * date:2019/7/9
 * <p>
 * 动态列查询工具类
 */
@Data
@Slf4j
@Component
public class FQUtil {

    /**
     * 获取父类及子类字段对应的列名集合
     */
    public static List<String> getFieldNames(Object obj) {
        Class clazz = obj.getClass();
        List<Field> fields = Arrays.asList(clazz.getDeclaredFields());
        List<Field> fieldList = getValidFields(fields);

        //父类
        //如果当前类继承了父类 将父类的字段添加进字段数组
        List<Field> superFieldList = addSuperFields(clazz);

        //遍历字段数组 获取非空字段名称 目前仅支持两层继承关系
        List<String> fieldNames = getFieldsWithHaveValue(fieldList, obj.getClass(), obj);
        List<String> superFieldNames = getFieldsWithHaveValue(superFieldList, obj.getClass().getSuperclass(), obj);
        fieldNames.addAll(superFieldNames);
        log.debug("【FQ】query fields:{}", fieldNames);
        return fieldNames;
    }

    /**
     * 如果有父类 添加父类字段
     *
     * @param clazz 当前类
     * @return 有效的父类字段集合
     */
    public static List<Field> addSuperFields(Class clazz) {
        List<Field> superFieldList = new ArrayList<>();
        return addSuperFields(clazz, superFieldList);
    }

    /**
     * 如果有父类 添加父类字段
     *
     * @param clazz  当前类
     * @param fields 当前类字段集合
     * @return 如果有父类则获取有效的父类字段集合，没有父类原数据返回
     */
    public static List<Field> addSuperFields(Class clazz, List<Field> fields) {
        if (clazz.getSuperclass() != null) {
            List<Field> superFieldList = Arrays.asList(clazz.getSuperclass().getDeclaredFields());
            fields = getValidFields(superFieldList, fields);
        }
        return fields;
    }

    /**
     * 获取类中有效的字段
     *
     * @param sourceFields 字段集合
     * @return 有效的字段集合
     */
    public static List<Field> getValidFields(List<Field> sourceFields) {
        List<Field> targetFields = new ArrayList<>();
        return getValidFields(sourceFields, targetFields);
    }

    /**
     * 获取类中有效的字段
     *
     * @param sourceFields 父类字段集合
     * @param targetFields 子类字段集合
     * @return 有效的字段集合
     */
    public static List<Field> getValidFields(List<Field> sourceFields, List<Field> targetFields) {
        for (Field field : sourceFields) {
            boolean hidden = false;
            // 使用swagger中@ApiModelProperty中的hidden属性甄别字段是否需要隐藏 隐藏的字段是不可以查询的
            if (field.isAnnotationPresent(ApiModelProperty.class)){
                ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
                hidden = apiModelProperty.hidden();
            }
            // FQ中自带的字段隐藏
            if (field.isAnnotationPresent(FieldHidden.class)){
                hidden = true;
            }
            // 排除序列化ID 和隐藏的字段
            if (!"serialVersionUID".equals(field.getName()) && !hidden) {
                targetFields.add(field);
            }
        }
        return targetFields;
    }


    /**
     * 对字段进行过滤 获取有值的字段集合
     *
     * @param fieldList 符合基础要求的字段集
     * @param clazz     字段所在的类
     * @param object    类
     * @return 字段名集合
     */
    public static List<String> getFieldsWithHaveValue(List<Field> fieldList, Class clazz, Object object) {
        List<String> fieldNames = new ArrayList<>();
        for (Field field : fieldList) {
            String name = field.getName();
            String firstChar = name.substring(0, 1);
            Method method;
            Object value = null;
            try {
                method = clazz.getDeclaredMethod("get" + firstChar.toUpperCase() + name.substring(1));
                value = method.invoke(object);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //如果该字段有内容，证明前端想获取该字段在数据库中对应数据
            if (value != null) {
                fieldNames.add(name);
            }
        }
        return fieldNames;
    }

    /**
     * 查询获取数据
     */
    public static List<Map<String, Object>> queryCol(String sql, JdbcTemplate jdbcTemplate) {
        List<Map<String, Object>> mapList = jdbcTemplate.queryForList(sql);

        List<Map<String, Object>> resultList = new ArrayList<>();
        //转换key值格式为驼峰命名
        for (int i = 0; i < mapList.size(); i++) {
            Map<String, Object> resultMap = new HashMap<>();
            Map<String, Object> map = mapList.get(i);
            Object[] keys = map.keySet().toArray();
            Object[] values = map.values().toArray();

            for (int j = 0; j < keys.length; j++) {
                resultMap.put(FQUtil.underLineToHumpNaming(keys[j].toString()), values[j]);
            }
            resultList.add(resultMap);
        }
        return resultList;
    }


    /**
     * 驼峰命名法-->下划线加小写命名法
     */
    public static String humpToUnderlineNaming(String param) {
        Pattern p = compile("[A-Z]");
        if (param == null || param.equals("")) {
            return "";
        }
        StringBuilder builder = new StringBuilder(param);
        Matcher mc = p.matcher(param);
        int i = 0;
        while (mc.find()) {
            builder.replace(mc.start() + i, mc.end() + i, "_" + mc.group().toLowerCase());
            i++;
        }

        if ('_' == builder.charAt(0)) {
            builder.deleteCharAt(0);
        }
        return builder.toString();
    }

    /**
     * 下划线转驼峰
     */
    public static String underLineToHumpNaming(String str) {
        Pattern linePattern = compile("_(\\w)");
        str = str.toLowerCase();
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


}
